#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <unistd.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include "Common.hpp"
#include <cstdlib>
#include "InetAddr.hpp"

namespace SocketModule
{
    class Socket;
#define DEFAULT_SOCKFD -1
#define BACKLOG 8
    using SockPtr = std::shared_ptr<Socket>;
    using namespace LogModule;

    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual void SocketOrDie() = 0;
        virtual bool BindOrDie(uint16_t port) = 0;
        virtual void SetSocket() = 0;
        virtual bool ListenOrDie() = 0;
        virtual int Accept(InetAddr *client, int *out_error) = 0;
        virtual void Close() = 0;
        virtual int Send(std::string &in) = 0;
        virtual int Recv(std::string *out) = 0;
        virtual int Fd() = 0;
        void BuildTcpSocket(uint16_t port)
        {
            SocketOrDie();

            SetSocket();
            BindOrDie(port);
            ListenOrDie();
        }

    private:
    };

    class TcpSocket : public Socket
    {
    public:
        TcpSocket(int sockfd = DEFAULT_SOCKFD)
            : _sockfd(sockfd)
        {
        }

        virtual ~TcpSocket() {}

        virtual void SocketOrDie() override
        {
            _sockfd = socket(AF_INET, SOCK_STREAM, 0);
            if (_sockfd < 0)
            {
                LOG(LogLevel::FATAL) << "create socket failed";
                exit(SOCKET_ERR);
            }
            LOG(LogLevel::DEBUG) << "create socket success";
            SetNonBlock(_sockfd);
        }
        virtual bool BindOrDie(uint16_t port) override
        {
            if (_sockfd == DEFAULT_SOCKFD)
                return false;
            InetAddr addr(port);
            int n = bind(_sockfd, addr.NetAddr(), addr.NetAddrLen());
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "bind socket failed";
                exit(BIND_ERR);
            }
            else
                LOG(LogLevel::DEBUG) << "bind socket success";
            return true;
        }
        virtual void SetSocket() override
        {
            // 保证服务器在异常断开后，可以立即重启
            int opt = 1;
            int n = ::setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof opt);
        }
        virtual bool ListenOrDie() override
        {
            if (_sockfd == DEFAULT_SOCKFD)
                return false;
            int n = listen(_sockfd, BACKLOG);
            if (n < 0)
            {
                LOG(LogLevel::FATAL) << "listen socket failed";
                exit(LISTEN_ERR);
            }
            else
            {
                LOG(LogLevel::DEBUG) << "listen socket success";
            }
            return true;
        }
        virtual int Accept(InetAddr *client, int *out_error) override
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof peer;
            int sockfd = accept(_sockfd, CONV(&peer), &len);
            *out_error = errno;
            if (sockfd < 0)
            {
                LOG(LogLevel::ERROR) << "accept socket failed";
                return -1;
            }
            else
            {
                LOG(LogLevel::DEBUG) << "accept socket success";
            }
            client->SetAddr(peer);
            return sockfd;
        }
        virtual void Close() override
        {
            if (_sockfd == DEFAULT_SOCKFD)
                return;
            close(_sockfd);
        }
        virtual int Send(std::string &in) override
        {
            auto size = send(_sockfd, in.c_str(), in.size(), 0);
            LOG(LogLevel::ERROR) << "_SOCKFD" << _sockfd;
            if (size < 0)
            {
                perror("err:");
                LOG(LogLevel::ERROR) << "send socket failed";
            }
            else
            {
                LOG(LogLevel::DEBUG) << "send socket success";
            }
            return size;
        }
        virtual int Recv(std::string *out) override
        {
            char buff[1024 * 8] = {0};
            auto size = recv(_sockfd, buff, sizeof(buff) - 1, 0);
            // //if(size<0)
            // //{
            //     LOG(LogLevel::ERROR) << "recv socket failed";
            // }
            // else
            if (size > 0)
            {
                buff[size] = 0;
                *out += buff;
                LOG(LogLevel::DEBUG) << "recv socket success";
            }
            return size;
        }
        virtual int Fd() override
        {
            return _sockfd;
        }

    private:
        int _sockfd;
    };
}
